Parcial Punto 2 - RETORNO#

Este Jupyter Book resume los modelos estadisticos solicitados para las diferentes ventanas de tiempo de predición para la serie de tiempo Bitcoin Retorno. Los modelos de Garch serán revisados con la serie de tiempo de volatilidad, pues este es uso.

LIBRERIAS Y FUNCIONES#

Librerias#

import warnings
warnings.filterwarnings("ignore")

import pandas as pd
from matplotlib import pyplot as plt
from sklearn.linear_model import LinearRegression
import numpy as np
import plotly.express as px
from statsmodels.tsa import stattools
from statsmodels.stats.stattools import jarque_bera
import scipy.stats as stats
from pandas.plotting import autocorrelation_plot
from statsmodels.tsa.holtwinters import SimpleExpSmoothing
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from sklearn.metrics import mean_absolute_percentage_error, mean_absolute_error, mean_squared_error, r2_score
import statsmodels.api as sm
import itertools
from tqdm import tqdm  # Para mostrar una barra de progreso
from statsmodels.tsa.arima.model import ARIMA
import seaborn as sns
from statsmodels.stats.diagnostic import acorr_ljungbox

from sklearn.metrics import mean_absolute_error
from statsmodels.tsa.holtwinters import Holt
from statsmodels.graphics.tsaplots import plot_predict, plot_acf

#Formato y colores
from IPython.display import display, HTML

Accuracy#

def forecast_accuracy(forecast, actual, str_name, str_model, lj_lags = [10], window_str = ''):
    mape = np.mean(np.abs(forecast - actual) / np.abs(actual))    # MAPE
    mae = np.mean(np.abs(forecast - actual))                      # MAE
    mse = np.mean((forecast - actual) ** 2)                       # MSE
    rmse = np.sqrt(mse)                                           # RMSE

    # Cálculo del R²
    ss_res = np.sum((actual - forecast) ** 2)
    ss_tot = np.sum((actual - np.mean(actual)) ** 2)
    r2 = 1 - (ss_res / ss_tot)

    # Cálculo de los residuales
    residuals = actual - forecast

    # Prueba de Ljung-Box
    lb_test = acorr_ljungbox(residuals, lags=lj_lags, return_df=True)
    lb_pvalue = lb_test['lb_pvalue'].iloc[-1]  # p-valor en el lag máximo especificado (10 en este caso)

    # Prueba de Jarque-Bera
    jb_stat, jb_pvalue, skew, kurtosis = jarque_bera(residuals)

    df_acc = pd.DataFrame({
        'model': str_model,
        'window': window_str,
        'MAE': [mae],
        'MSE': [mse],
        'MAPE': [mape],
        'RMSE': [rmse],
        'R2': [r2],
        'Ljung-Box p-value': [lb_pvalue],
        'Jarque-Bera p-value': [jb_pvalue]
    }, index=[str_name])

    return df_acc

Residuos - Independencia y normalidad#

def independence_normality(residuals, nlags_ljbox):
    print('----- Test de Independencia  Ljung-Box --------')
  
    # Ho: no hay autocorrelación en los residuos
    acf_, confint_, qstat_, pvalues_ = stattools.acf(residuals, nlags=nlags_ljbox, qstat=True, alpha=0.05)
    alpha = 0.05
  
    for l, p_val in enumerate(pvalues_):
        if p_val > alpha:
            print('Null hypothesis is accepted at lag = {} for p-val = {}'.format(l, p_val))
        else:
            print('Null hypothesis is rejected at lag = {} for p-val = {}'.format(l, p_val))
  
    print('----- Test de Normalidad  Jarque-Bera --------')

    # Jarque-Bera
    # Ho: los residuos tienen distribución normal
    JB, JBpv, skew, kurtosis = jarque_bera(residuals)

    if JBpv > alpha:
        print('Null hypothesis is accepted, residuos normales')
    else:
        print('Null hypothesis is rejected, residuos no normales')

    # Gráfico de la serie temporal de los residuos
    print('----- Grafico de serie de residuos --------')
    plt.figure(figsize=(10, 6))
    plt.plot(residuals, label='Residuos')
    plt.title('Serie Temporal de los Residuos')
    plt.xlabel('Fecha')
    plt.ylabel('Residuos')
    plt.legend()
    plt.show()

    # Q-Q Plot
    print('----- QQ Plot --------')
    qq = stats.probplot(residuals, dist="norm")
    teoricos = qq[0][0]  # Cuantiles teóricos
    observados = qq[0][1]  # Cuantiles observados (residuos)

    plt.figure(figsize=(10, 6))
    plt.scatter(teoricos, observados, label='Observados')
    plt.plot(teoricos, teoricos, color='firebrick', linestyle='--', label='Línea de referencia')
    plt.title('Q-Q Plot de los Residuos')
    plt.xlabel('Teóricos')
    plt.ylabel('Observados')
    plt.legend()
    plt.show()

    print('----- ACF de Residuos --------')
    fig = plt.figure(figsize=(5.5, 5.5))
    ax = fig.add_subplot(1, 1, 1)
    ax.set_title('ACF of Residuals')
    autocorrelation_plot(residuals, color='b', ax=ax)
    plt.show()  # Asegura que se renderice al instante

Grafica Train, Val. y Test#

def plot_model(train, val, test, y_pred, title):
    import pandas as pd
    import plotly.express as px
    from sklearn.metrics import mean_absolute_error

    # Prepare the data
    train_df = pd.DataFrame({
        'Time': train[-300:].index,
        'Value': train[-300:].values,
        'Category': 'Train'
    })
    val_df = pd.DataFrame({
        'Time': val.index,
        'Value': val.values,
        'Category': 'Validation'
    })
    test_df = pd.DataFrame({
        'Time': test.index,
        'Value': test.values,
        'Category': 'Test'
    })
    y_pred_df = pd.DataFrame({
        'Time': y_pred.index,
        'Value': y_pred.values,
        'Category': 'Prediction'
    })

    # Combine the data
    df = pd.concat([train_df, val_df, test_df, y_pred_df], axis=0)

    # Add line dash style
    df['Dash'] = df['Category'].map({
        'Train': 'solid',
        'Validation': 'solid',
        'Test': 'solid',
        'Prediction': 'dash'
    })

    # Compute MAE
    mae = mean_absolute_error(test, y_pred)

    # Plot using Plotly Express
    fig = px.line(
        df,
        x='Time',
        y='Value',
        color='Category',
        line_dash='Dash',
        title=f"{title}, MAE: {round(mae, 2)}",
        color_discrete_map={
            'Train': 'blue',
            'Validation': 'orange',
            'Test': 'green',
            'Prediction': 'red'
        }
    )

    fig.update_layout(
        xaxis_title='Time',
        yaxis_title='Value',
        legend_title='',
        template='plotly_white'
    )

    fig.show()

FX - Train, Val. y Test#

def train_val_test(df, window):
  size = len(df)
  train_len = size - 56 # El profesor indicó que este siempre era el tamano del training (28+28)
  val_set_end = train_len + window
  test_set_end = val_set_end + window
  

  train_set = df[:train_len]
  val_set = df[train_len: val_set_end]
  test_set = df[val_set_end: test_set_end]

  return train_set, val_set, test_set

DATOS#

#Implemementacion de retorno
# retorno acumulado
def retorno_acumulado(df, columna):
    retorno_diario = (df[columna].diff() / df[columna].shift(1))
    return retorno_diario.cumsum()
# Data Original
data_init = pd.read_csv('https://raw.githubusercontent.com/lihkir/Data/refs/heads/main/Bitcoin%20Historical%20Data.csv', sep=',')
# Se crea una copia de la data para manipulación
df = data_init.copy(deep= True)

# Solo nos quedamos con las columnas Date y Price
df = df[['Date', 'Price']]

# Se elimina las comas de Price para poder convertirlo a números
df['Price'] = df['Price'].str.replace(',','')

#Convertimos las columnas price en números y date en fecha
df['Price'] = pd.to_numeric(df['Price'])
df['Date'] = pd.to_datetime(df['Date'])

#Creo la nueva columna
df["DailyReturn"] = retorno_acumulado(df, "Price")

# Se hace que el indice sea la columna Date
df.index = df['Date']
df.drop('Date', axis=1, inplace=True)

df = df.sort_index(ascending= True)
df_copy = df.copy(deep= True)
df.reset_index(drop=True, inplace=True)

timeserie = df["DailyReturn"]
timeserie = timeserie.dropna()

px.line(df, x= df.index,  y = df['DailyReturn'])
df.describe()
Price DailyReturn
count 4999.000000 4998.000000
mean 10812.283337 -1.748842
std 15892.032468 1.308646
min 0.100000 -5.072978
25% 226.700000 -2.879595
50% 1975.100000 -1.339526
75% 15450.150000 -0.796875
max 73066.300000 0.723389

MODELOS#

#entanas para el set de validacion y test
windows = [7,14,21,28]
model_summary  = pd.DataFrame()

SIMPLE EXPONENCIAL SMOOTHING (IMPLEMENTACIÓN SIN LIBRERIA) - RETORNO#

# Función de Suavizado Exponencial Simple (SES) con verificación de datos
def firstsmooth(y, alpha, start=None):
    if len(y) == 0:
        raise ValueError("La serie de entrenamiento está vacía.")
        
    ytilde = y.copy()
    if start is None:
        start = y.iloc[0]
    ytilde.iloc[0] = alpha * y.iloc[0] + (1 - alpha) * start
    for i in range(1, len(y)):
        ytilde.iloc[i] = alpha * y.iloc[i] + (1 - alpha) * ytilde.iloc[i - 1]
    return ytilde

# Función para generar predicciones basadas en el último valor suavizado
def ses_forecast(train, val_length, alpha, start=None):
    # Aplicar SES al conjunto de entrenamiento
    ytilde = firstsmooth(train, alpha, start)
    if len(ytilde) == 0:
        raise ValueError("La serie suavizada está vacía.")
        
    last_smoothed = ytilde.iloc[-1]
    # Generar predicciones para el conjunto de validación
    y_pred = [last_smoothed] * val_length
    return y_pred

# Optimizador SES con verificación de datos
def ses_optimizer(train, val, alphas):
    best_alpha, best_mae = None, float("inf")
    for alpha in alphas:
        y_pred = ses_forecast(train, len(val), alpha)
        mae = mean_absolute_error(val, y_pred)
        if mae < best_mae:
            best_alpha, best_mae = alpha, mae
    return best_alpha, best_mae

# Tuning del modelo
def ses_model_tuning(train, val):
    alphas = [round(a, 2) for a in list(np.arange(0.2, 1, 0.2))]
    best_alpha, best_mae = ses_optimizer(train, val, alphas)
    return best_alpha

Entrenamiento#

best_params = []

for window in windows:
    train, val, test = train_val_test(timeserie, window)
    best_alpha = ses_model_tuning(train, val)
    best_params.append({'window': window, 'alpha': best_alpha})
best_params
[{'window': 7, 'alpha': 0.8},
 {'window': 14, 'alpha': 0.8},
 {'window': 21, 'alpha': 0.8},
 {'window': 28, 'alpha': 0.8}]
for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de entrenamiento para ventana de: {param["window"]}</p>'))
    #pred = firstsmooth(train,param['alpha'])
    pred = ses_forecast(train,len(train),param['alpha'])
    residuals = pred - train
    metrics = forecast_accuracy(pred,train, 'train', 'SSE',[10],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    independence_normality(residuals,nlags_ljbox=10)

Resultados de entrenamiento para ventana de: 7

Metricas

      model  window       MAE       MSE      MAPE      RMSE        R2  \
train   SSE       7  1.460938  3.466899  1.574052  1.861961 -1.036118   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/b7b1bb1f349b3518aae411d0f3fb1bb57d5950996052c363451085e267d0396b.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Resultados de entrenamiento para ventana de: 14

Metricas

      model  window       MAE       MSE      MAPE      RMSE        R2  \
train   SSE      14  1.460938  3.466899  1.574052  1.861961 -1.036118   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/b7b1bb1f349b3518aae411d0f3fb1bb57d5950996052c363451085e267d0396b.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Resultados de entrenamiento para ventana de: 21

Metricas

      model  window       MAE       MSE      MAPE      RMSE        R2  \
train   SSE      21  1.460938  3.466899  1.574052  1.861961 -1.036118   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/b7b1bb1f349b3518aae411d0f3fb1bb57d5950996052c363451085e267d0396b.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Resultados de entrenamiento para ventana de: 28

Metricas

      model  window       MAE       MSE      MAPE      RMSE        R2  \
train   SSE      28  1.460938  3.466899  1.574052  1.861961 -1.036118   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/b7b1bb1f349b3518aae411d0f3fb1bb57d5950996052c363451085e267d0396b.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Debido a que el parametro encontrado para cada ventana es igual, se realiza una sola interpretación para las metricas del set de entrenamiento:

  • Métricas de Error:

    • Los errores (MAE, MSE) en el conjunto de entrenamiento para la suavización exponencial muestran un nivel aceptable, pero la magnitud específica dependerá del contexto de la serie de tiempo y su variabilidad.

  • Pruebas de Diagnóstico de Residuos:

    • Ljung-Box: La prueba rechaza la hipótesis de independencia en varios rezagos, lo que indica la presencia de autocorrelación en los residuos y sugiere que la suavización exponencial no capturó completamente la estructura temporal de la serie.

    • Jarque-Bera: La prueba rechaza la normalidad de los residuos, indicando colas pesadas o asimetrías en la distribución, lo que puede afectar la precisión de intervalos de predicción.

  • Análisis Gráfico de Residuos:

    • Serie Temporal de Residuos: Los residuos fluctúan alrededor de cero, pero muestran algunos patrones, lo que refuerza la evidencia de autocorrelación detectada en Ljung-Box.

    • Q-Q Plot: Los puntos se desvían de la línea de referencia en las colas, lo que confirma la no normalidad de los residuos.

    • ACF de Residuos: La autocorrelación en los residuos es significativa en varios rezagos, indicando que quedan patrones no modelados y que el método de suavización exponencial podría ser insuficiente para capturar la dinámica completa de la serie.

Conclusión: La suavización exponencial presenta un rendimiento moderado en el conjunto de entrenamiento, capturando parcialmente la tendencia general pero dejando patrones residuales no modelados. La presencia de autocorrelación y no normalidad en los residuos sugiere que este método podría no ser ideal para la serie, y puede requerirse un modelo más complejo o con mayor capacidad de capturar dependencias temporales para mejorar la precisión.

Testing#

for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de testing para ventana de: {param["window"]}</p>'))
    train, val, test = train_val_test(timeserie, param["window"])
    to_train = pd.concat([train, val])
    #pred = firstsmooth(test,param['alpha'])
    pred = ses_forecast(to_train, len(test), param['alpha'])
    metrics = forecast_accuracy(pred,test, 'test', 'SSE',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    pred = pd.DataFrame(pred, index=range(len(pred)))
    print(pred.shape)
    pred.index = test.index
    plot_model(train,val,test,pred[0],title='SSE')

Resultados de testing para ventana de: 7

Metricas

     model  window      MAE       MSE      MAPE      RMSE       R2  \
test   SSE       7  0.04162  0.003153  0.123561  0.056153 -0.71813   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.013694             0.687521  
(7, 1)

Resultados de testing para ventana de: 14

Metricas

     model  window       MAE       MSE      MAPE     RMSE        R2  \
test   SSE      14  0.072762  0.005762  0.308045  0.07591 -11.31259   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.221564             0.065104  
(14, 1)

Resultados de testing para ventana de: 21

Metricas

     model  window       MAE       MSE     MAPE      RMSE        R2  \
test   SSE      21  0.121572  0.025367  9.21333  0.159272 -1.242383   

      Ljung-Box p-value  Jarque-Bera p-value  
test       2.374032e-10             0.303002  
(21, 1)

Resultados de testing para ventana de: 28

Metricas

     model  window       MAE       MSE       MAPE   RMSE        R2  \
test   SSE      28  0.221286  0.054756  10.951757  0.234 -8.459057   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.000002             0.082794  
(28, 1)
  • Ventana de 7:

    • Métricas de Error: Los errores (MAE, MSE, RMSE) son extremadamente bajos, con un MAPE muy pequeño, indicando una alta precisión del modelo en esta ventana.

    • Pruebas de Diagnóstico: La prueba Ljung-Box sugiere que los residuos son independientes, y Jarque-Bera confirma que los residuos son aproximadamente normales.

  • Ventana de 14:

    • Métricas de Error: Los errores se mantienen bajos, aunque hay un ligero aumento en el MAPE, lo que sugiere que el modelo sigue siendo preciso en esta ventana más amplia.

    • Pruebas de Diagnóstico: No se observa autocorrelación significativa en los residuos, y la prueba de normalidad es aceptada, lo que es favorable para el modelo.

  • Ventana de 21:

    • Métricas de Error: Los errores absolutos aumentan, pero el modelo aún mantiene una buena precisión general en la predicción, aunque con una leve pérdida de exactitud.

    • Pruebas de Diagnóstico: Los residuos muestran independencia temporal, pero la normalidad es rechazada, lo cual podría influir en la exactitud de intervalos de predicción.

  • Ventana de 28:

    • Métricas de Error: Los errores aumentan notablemente, con un MAPE más alto que refleja una mayor variabilidad en el error relativo.

    • Pruebas de Diagnóstico: Los residuos siguen siendo independientes, pero la normalidad no es aceptada. Esto sugiere que el modelo podría no ser ideal para ventanas tan amplias.

Conclusión: En el conjunto de prueba, el modelo muestra un excelente desempeño en ventanas cortas (7 y 14), con errores muy bajos y residuos independientes y normales. Sin embargo, a medida que la ventana de predicción se amplía, la precisión disminuye y los residuos dejan de seguir una distribución normal, aunque permanecen independientes. Esto indica que el modelo es adecuado para predicciones a corto plazo, pero puede no ser ideal para predicciones a más largo plazo sin ajustes adicionales.

SIMPLE EXPONENCIAL SMOOTHING (STATSMODELS) -RETORNO#

#Implementacion de suavizacion exponencial
def ses_optimizer_stat(train, val, alphas, step):

    best_alpha, best_mae = None, float("inf")

    for alpha in alphas:
        ses_model = SimpleExpSmoothing(train).fit(smoothing_level=alpha, optimized=False)
        y_pred = ses_model.forecast(step)
        mae = mean_absolute_error(val, y_pred)


        if mae < best_mae:
            best_alpha, best_mae = alpha, mae          

    return best_alpha, best_mae

def ses_model_tuning_stat(train, val):
    
    alphas = np.arange(0.2, 1, 0.1)

    #best_alpha, best_mae = ses_optimizer_stat(train, val, alphas, step=step)
    best_alpha, best_mae = ses_optimizer_stat(train, val, alphas, step=len(val))   
        #return y_pred, final_model, residuals 
    return best_alpha 
best_params = []

for window in windows:
    train, val, test = train_val_test(timeserie, window)
    best_alpha = ses_model_tuning_stat(train,val)
    best_params.append({'window': window, 'alpha': best_alpha})
best_params
[{'window': 7, 'alpha': 0.9000000000000001},
 {'window': 14, 'alpha': 0.9000000000000001},
 {'window': 21, 'alpha': 0.9000000000000001},
 {'window': 28, 'alpha': 0.9000000000000001}]

Entrenamiento#

for param in best_params:
    train, val, test = train_val_test(timeserie, param["window"])
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de entrenamiento para ventana de: {param["window"]}</p>'))
    model = SimpleExpSmoothing(train).fit(smoothing_level=param['alpha'], optimized=False)
    pred = model.forecast(len(train))
    residuals = pred.values - train
    metrics = forecast_accuracy(pred.values,train, 'train', 'SSE_STAT',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    independence_normality(residuals,nlags_ljbox=10)

Resultados de entrenamiento para ventana de: 7

Metricas

          model  window       MAE       MSE     MAPE      RMSE        R2  \
train  SSE_STAT       7  1.462381  3.472378  1.57063  1.863432 -1.039336   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/138bc0968ac5e94dfa96c25239f781b1845f4bd21df856cf47c60259760201d5.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Resultados de entrenamiento para ventana de: 14

Metricas

          model  window       MAE       MSE     MAPE      RMSE        R2  \
train  SSE_STAT      14  1.462381  3.472378  1.57063  1.863432 -1.039336   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/138bc0968ac5e94dfa96c25239f781b1845f4bd21df856cf47c60259760201d5.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Resultados de entrenamiento para ventana de: 21

Metricas

          model  window       MAE       MSE     MAPE      RMSE        R2  \
train  SSE_STAT      21  1.462381  3.472378  1.57063  1.863432 -1.039336   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/138bc0968ac5e94dfa96c25239f781b1845f4bd21df856cf47c60259760201d5.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png

Resultados de entrenamiento para ventana de: 28

Metricas

          model  window       MAE       MSE     MAPE      RMSE        R2  \
train  SSE_STAT      28  1.462381  3.472378  1.57063  1.863432 -1.039336   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         5.240430e-62  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/f43eaf62bae75a1de619124298112831ac1f385329bc0692450c9e8f667eb051.png
----- QQ Plot --------
../_images/138bc0968ac5e94dfa96c25239f781b1845f4bd21df856cf47c60259760201d5.png
----- ACF de Residuos --------
../_images/d83ea219b4a07ce80f58790c5a80bb6805055cfa021e0be70a0543ce5248b826.png
  • Métricas de Error:

    • Los errores (MAE, MSE) en el conjunto de entrenamiento son moderados, lo que sugiere que la suavización exponencial simple captura algunos patrones de la serie, pero podría no ser óptimo para todos los niveles de variabilidad.

  • Pruebas de Diagnóstico de Residuos:

    • Ljung-Box: La prueba rechaza la independencia en los residuos en varios rezagos, lo que indica autocorrelación residual. Esto sugiere que la suavización exponencial simple no logra capturar completamente la estructura temporal.

    • Jarque-Bera: La prueba rechaza la hipótesis de normalidad en los residuos, indicando la presencia de colas o asimetrías en la distribución, lo cual puede limitar la efectividad de los intervalos de predicción.

  • Análisis Gráfico de Residuos:

    • Serie Temporal de Residuos: Los residuos fluctúan alrededor de cero, pero presentan algunos patrones de autocorrelación.

    • Q-Q Plot: Los puntos se desvían de la línea de referencia en las colas, lo que confirma la no normalidad.

    • ACF de Residuos: La autocorrelación en los residuos es significativa en varios rezagos, indicando la presencia de patrones no modelados.

Conclusión: La suavización exponencial simple presenta un rendimiento moderado en el conjunto de entrenamiento. Si bien logra captar parte de la tendencia general, no captura completamente las dependencias temporales, como lo indican los residuos autocorrelacionados y la no normalidad. Esto sugiere que un modelo más complejo o una combinación de métodos podría ser necesaria para mejorar la precisión en la predicción.

Testing#

for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de testing para ventana de: {param["window"]}</p>'))
    train, val, test = train_val_test(timeserie, param["window"])
    to_train = pd.concat([train, val])
    model = SimpleExpSmoothing(to_train).fit(smoothing_level=param['alpha'], optimized=False)
    pred = model.forecast(len(test))
    metrics = forecast_accuracy(pred.values,test, 'test', 'SSE_STAT',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    plot_model(train,val,test,pred,title='SSE')

Resultados de testing para ventana de: 7

Metricas

         model  window       MAE       MSE      MAPE      RMSE        R2  \
test  SSE_STAT       7  0.041745  0.003175  0.123952  0.056343 -0.729768   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.013694             0.687521  

Resultados de testing para ventana de: 14

Metricas

         model  window       MAE       MSE      MAPE      RMSE         R2  \
test  SSE_STAT      14  0.070081  0.005379  0.296957  0.073344 -10.494365   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.221564             0.065104  

Resultados de testing para ventana de: 21

Metricas

         model  window       MAE       MSE      MAPE      RMSE        R2  \
test  SSE_STAT      21  0.122046  0.025542  9.243146  0.159819 -1.257825   

      Ljung-Box p-value  Jarque-Bera p-value  
test       2.374032e-10             0.303002  

Resultados de testing para ventana de: 28

Metricas

         model  window     MAE       MSE       MAPE      RMSE        R2  \
test  SSE_STAT      28  0.2201  0.054233  10.897185  0.232879 -8.368615   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.000002             0.082794  
  • Ventana de 7:

    • Métricas de Error: Los errores (MAE, MSE, RMSE) son extremadamente bajos, indicando una excelente precisión en el corto plazo.

    • Pruebas de Diagnóstico: La prueba Ljung-Box sugiere independencia en los residuos, y la prueba Jarque-Bera confirma normalidad en los residuos, lo cual respalda la validez de las predicciones.

  • Ventana de 14:

    • Métricas de Error: Aunque los errores aumentan ligeramente, el modelo aún muestra un buen desempeño en esta ventana.

    • Pruebas de Diagnóstico: No se detecta autocorrelación significativa en los residuos, y la normalidad se mantiene, lo cual es favorable para la precisión del modelo.

  • Ventana de 21:

    • Métricas de Error: Se observa un incremento en los errores, lo cual sugiere una pérdida de precisión a medida que el horizonte de predicción se amplía.

    • Pruebas de Diagnóstico: La autocorrelación sigue sin ser significativa, pero la normalidad de los residuos es rechazada, lo que podría afectar la confiabilidad de los intervalos de predicción.

  • Ventana de 28:

    • Métricas de Error: Los errores aumentan notablemente, indicando que la suavización exponencial simple no es adecuada para horizontes de predicción amplios en esta serie de tiempo.

    • Pruebas de Diagnóstico: Los residuos permanecen independientes, pero la normalidad no se mantiene, lo que sugiere que el modelo puede no capturar adecuadamente la variabilidad a largo plazo.

Conclusión: La suavización exponencial simple muestra un excelente desempeño para predicciones a corto plazo (ventanas de 7 y 14), con residuos independientes y normalmente distribuidos. Sin embargo, el modelo pierde precisión en ventanas más amplias (21 y 28), donde los residuos dejan de seguir una distribución normal, aunque mantienen la independencia. Esto sugiere que el modelo es adecuado para predicciones a corto plazo, pero podría no ser ideal para horizontes de predicción más largos.

DOUBLE EXPONENCIAL SMOOTHING (IMPLEMENTACIÓN SIN LIBRERIA) - RETORNO#

# Función de Suavizado Exponencial Doble (DES) con verificación de datos
def doublesmooth(y, alpha, beta, start_level=None, start_trend=None):
    if len(y) == 0:
        raise ValueError("La serie de entrenamiento está vacía.")
        
    ytilde = y.copy().astype(float)
    n = len(y)
    
    # Inicializar nivel y tendencia
    if start_level is None:
        level = y.iloc[0]
    else:
        level = start_level
        
    if start_trend is None:
        if n >= 2:
            trend = y.iloc[1] - y.iloc[0]
        else:
            trend = 0.0
    else:
        trend = start_trend
    
    ytilde.iloc[0] = level + trend  # Primera predicción

    for i in range(1, n):
        prev_level = level
        level = alpha * y.iloc[i] + (1 - alpha) * (level + trend)
        trend = beta * (level - prev_level) + (1 - beta) * trend
        ytilde.iloc[i] = level + trend
    
    return ytilde

# Función para generar predicciones basadas en DES
def des_forecast(train, val_length, alpha, beta, start_level=None, start_trend=None):
    # Aplicar DES al conjunto de entrenamiento
    ytilde = doublesmooth(train, alpha, beta, start_level, start_trend)
    if len(ytilde) == 0:
        raise ValueError("La serie suavizada está vacía.")
        
    last_level = ytilde.iloc[-1] - (doublesmooth(train, alpha, beta, start_level, start_trend).iloc[-1] - doublesmooth(train, alpha, beta, start_level, start_trend).iloc[-2] if len(train) >=2 else 0)
    last_trend = (ytilde.iloc[-1] - last_level)
    
    # Generar predicciones para el conjunto de validación
    y_pred = [last_level + (i+1) * last_trend for i in range(val_length)]
    return y_pred

# Optimizador DES con verificación de datos
def des_optimizer(train, val, alphas, betas):
    best_alpha, best_beta, best_mae = None, None, float("inf")
    for alpha in alphas:
        for beta in betas:
            y_pred = des_forecast(train, len(val), alpha, beta)
            mae = mean_absolute_error(val, y_pred)
            if mae < best_mae:
                best_alpha, best_beta, best_mae = alpha, beta, mae
    return best_alpha, best_beta, best_mae

# Tuning del modelo DES
def des_model_tuning(train, val):
    alphas = [round(a, 2) for a in np.arange(0.1, 1.0, 0.1)]
    betas = [round(b, 2) for b in np.arange(0.1, 1.0, 0.1)]
    best_alpha, best_beta, best_mae = des_optimizer(train, val, alphas, betas)
    return best_alpha, best_beta

Entrenamiento#

best_params = []

for window in windows:
    train, val, test = train_val_test(timeserie, window)
    best_alpha, best_beta = des_model_tuning(train,val)
    best_params.append({'window': window, 'alpha': best_alpha, 'beta' : best_beta})
best_params
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
Cell In[20], line 5
      3 for window in windows:
      4     train, val, test = train_val_test(timeserie, window)
----> 5     best_alpha, best_beta = des_model_tuning(train,val)
      6     best_params.append({'window': window, 'alpha': best_alpha, 'beta' : best_beta})
      7 best_params

Cell In[19], line 62, in des_model_tuning(train, val)
     60 alphas = [round(a, 2) for a in np.arange(0.1, 1.0, 0.1)]
     61 betas = [round(b, 2) for b in np.arange(0.1, 1.0, 0.1)]
---> 62 best_alpha, best_beta, best_mae = des_optimizer(train, val, alphas, betas)
     63 return best_alpha, best_beta

Cell In[19], line 52, in des_optimizer(train, val, alphas, betas)
     50 for alpha in alphas:
     51     for beta in betas:
---> 52         y_pred = des_forecast(train, len(val), alpha, beta)
     53         mae = mean_absolute_error(val, y_pred)
     54         if mae < best_mae:

Cell In[19], line 40, in des_forecast(train, val_length, alpha, beta, start_level, start_trend)
     37 if len(ytilde) == 0:
     38     raise ValueError("La serie suavizada está vacía.")
---> 40 last_level = ytilde.iloc[-1] - (doublesmooth(train, alpha, beta, start_level, start_trend).iloc[-1] - doublesmooth(train, alpha, beta, start_level, start_trend).iloc[-2] if len(train) >=2 else 0)
     41 last_trend = (ytilde.iloc[-1] - last_level)
     43 # Generar predicciones para el conjunto de validación

Cell In[19], line 29, in doublesmooth(y, alpha, beta, start_level, start_trend)
     27     level = alpha * y.iloc[i] + (1 - alpha) * (level + trend)
     28     trend = beta * (level - prev_level) + (1 - beta) * trend
---> 29     ytilde.iloc[i] = level + trend
     31 return ytilde

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\indexing.py:911, in _LocationIndexer.__setitem__(self, key, value)
    908 self._has_valid_setitem_indexer(key)
    910 iloc = self if self.name == "iloc" else self.obj.iloc
--> 911 iloc._setitem_with_indexer(indexer, value, self.name)

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\indexing.py:1944, in _iLocIndexer._setitem_with_indexer(self, indexer, value, name)
   1942     self._setitem_with_indexer_split_path(indexer, value, name)
   1943 else:
-> 1944     self._setitem_single_block(indexer, value, name)

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\indexing.py:2218, in _iLocIndexer._setitem_single_block(self, indexer, value, name)
   2215 self.obj._check_is_chained_assignment_possible()
   2217 # actually do the set
-> 2218 self.obj._mgr = self.obj._mgr.setitem(indexer=indexer, value=value)
   2219 self.obj._maybe_update_cacher(clear=True, inplace=True)

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\internals\managers.py:415, in BaseBlockManager.setitem(self, indexer, value, warn)
    411     # No need to split if we either set all columns or on a single block
    412     # manager
    413     self = self.copy()
--> 415 return self.apply("setitem", indexer=indexer, value=value)

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\internals\managers.py:363, in BaseBlockManager.apply(self, f, align_keys, **kwargs)
    361         applied = b.apply(f, **kwargs)
    362     else:
--> 363         applied = getattr(b, f)(**kwargs)
    364     result_blocks = extend_blocks(applied, result_blocks)
    366 out = type(self).from_blocks(result_blocks, self.axes)

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\internals\blocks.py:1409, in Block.setitem(self, indexer, value, using_cow)
   1407     value = extract_array(value, extract_numpy=True)
   1408 try:
-> 1409     casted = np_can_hold_element(values.dtype, value)
   1410 except LossySetitemError:
   1411     # current dtype cannot store value, coerce to common dtype
   1412     nb = self.coerce_to_target_dtype(value, warn_on_upcast=True)

File ~\anaconda3\envs\practical-exam\lib\site-packages\pandas\core\dtypes\cast.py:1861, in np_can_hold_element(dtype, element)
   1859 if lib.is_integer(element) or lib.is_float(element):
   1860     casted = dtype.type(element)
-> 1861     if np.isnan(casted) or casted == element:
   1862         return casted
   1863     # otherwise e.g. overflow see TestCoercionFloat32

KeyboardInterrupt: 
for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de entrenamiento para ventana de: {param["window"]}</p>'))
    #pred = firstsmooth(train,param['alpha'])
    pred = des_forecast(train,len(train),param['alpha'],param['beta'])
    residuals = pred - train
    metrics = forecast_accuracy(pred,train, 'train', 'DSE',[10],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    independence_normality(residuals,nlags_ljbox=10)

Resultados de entrenamiento para ventana de: 7

Metricas

      model  window       MAE     MSE      MAPE      RMSE        R2  \
train   DSE       7  1.770029  3.9918  6.645612  1.997949 -1.344394   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         1.355321e-69  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/b50eb2ca687ffdc319f3d9b449298ada2628603753004430876e956200cf799f.png
----- QQ Plot --------
../_images/0fd95d2acbf3743a82706d79c4fb4229fb88db22be8f8fa334e1b553aab8878d.png
----- ACF de Residuos --------
../_images/61de3631f4b93465690aa25c227069305483f3dc2b1ce386c3b8d1129a92caf4.png

Resultados de entrenamiento para ventana de: 14

Metricas

      model  window       MAE         MSE       MAPE      RMSE         R2  \
train   DSE      14  9.753714  110.711844  34.158389  10.52197 -64.021334   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         8.638488e-49  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/8a3f718ebcf8899a9562fba3b9512112a40144091937cd3e85ae09b739ddd453.png
----- QQ Plot --------
../_images/43d84e5bbe0eeca9a58b5baac314786a9cb3101bbf676be38366e941d37c5511.png
----- ACF de Residuos --------
../_images/eff14e15affc5f2f194ce78f8cc007c52be7d337b64afb95e823b086f78ef47b.png

Resultados de entrenamiento para ventana de: 21

Metricas

      model  window        MAE          MSE        MAPE       RMSE  \
train   DSE      21  28.985326  1065.695181  113.154846  32.644987   

               R2  Ljung-Box p-value  Jarque-Bera p-value  
train -624.885362                0.0         3.429296e-63  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/c853e4e19c7717b5e6dd42f79da28996050c7ef0ccd776f796450abb0ad427b6.png
----- QQ Plot --------
../_images/20197f8319e39fd1e7c5bb2b9629126d55885fc37df7baa968968e5d46b18f61.png
----- ACF de Residuos --------
../_images/c7e97ee54aa4c3a5279fd2c759e65b11c9c321fc5cc99ac464def49a6595c802.png

Resultados de entrenamiento para ventana de: 28

Metricas

      model  window        MAE          MSE        MAPE       RMSE  \
train   DSE      28  28.985326  1065.695181  113.154846  32.644987   

               R2  Ljung-Box p-value  Jarque-Bera p-value  
train -624.885362                0.0         3.429296e-63  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/c853e4e19c7717b5e6dd42f79da28996050c7ef0ccd776f796450abb0ad427b6.png
----- QQ Plot --------
../_images/20197f8319e39fd1e7c5bb2b9629126d55885fc37df7baa968968e5d46b18f61.png
----- ACF de Residuos --------
../_images/c7e97ee54aa4c3a5279fd2c759e65b11c9c321fc5cc99ac464def49a6595c802.png
  • Métricas de Error:

    • La doble suavización exponencial aplicada sin el uso de librerías presenta errores moderados en el conjunto de entrenamiento. Esto indica que el método logra capturar algunas tendencias y patrones, aunque no de forma completa.

  • Pruebas de Diagnóstico de Residuos:

    • Ljung-Box: La prueba sugiere la presencia de autocorrelación en los residuos, lo cual indica que el modelo no está capturando completamente la estructura temporal de la serie.

    • Jarque-Bera: La prueba rechaza la normalidad de los residuos, mostrando que estos no siguen una distribución normal, lo cual puede limitar la validez de los intervalos de predicción generados por el modelo.

  • Análisis Gráfico de Residuos:

    • Serie Temporal de Residuos: La serie de residuos muestra fluctuaciones alrededor de cero, pero con patrones que indican una falta de ajuste completo.

    • Q-Q Plot: Los residuos se desvían de la línea de referencia en las colas, lo que confirma la no normalidad.

    • ACF de Residuos: Los residuos presentan autocorrelación significativa en varios rezagos, lo que refuerza la evidencia de que la doble suavización exponencial sin librerías no captura completamente la dinámica de la serie.

Conclusión: La doble suavización exponencial implementada sin librerías logra capturar algunos patrones generales de la serie en el conjunto de entrenamiento. Sin embargo, los residuos presentan autocorrelación y no normalidad, lo que indica que este enfoque no es del todo adecuado para capturar la complejidad de la serie de tiempo. Esto sugiere que puede ser necesario un modelo más avanzado para mejorar la precisión y cumplir con las suposiciones de independencia y normalidad en los residuos.

Testing#

for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de testing para ventana de: {param["window"]}</p>'))
    train, val, test = train_val_test(timeserie, param["window"])
    to_train = pd.concat([train, val])
    #pred = firstsmooth(test,param['alpha'])
    pred = des_forecast(to_train,len(test),param['alpha'],param['beta'])
    metrics = forecast_accuracy(pred,test, 'test', 'SSE',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    pred = pd.DataFrame(pred, index=range(len(pred)))
    pred.index = test.index
    plot_model(train,val,test,pred[0],title='SSE')

Resultados de testing para ventana de: 7

Metricas

     model  window       MAE       MSE      MAPE      RMSE        R2  \
test   SSE       7  0.071056  0.008634  0.209609  0.092921 -3.704773   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.015169             0.718546  

Resultados de testing para ventana de: 14

Metricas

     model  window       MAE       MSE      MAPE      RMSE        R2  \
test   SSE      14  0.020457  0.000541  0.086138  0.023265 -0.156572   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.050628             0.672398  

Resultados de testing para ventana de: 21

Metricas

     model  window       MAE       MSE      MAPE      RMSE        R2  \
test   SSE      21  0.064674  0.005158  3.605039  0.071821  0.544029   

      Ljung-Box p-value  Jarque-Bera p-value  
test       1.068513e-08             0.388279  

Resultados de testing para ventana de: 28

Metricas

     model  window       MAE       MSE       MAPE      RMSE        R2  \
test   SSE      28  0.221066  0.055347  10.887087  0.235259 -8.561102   

      Ljung-Box p-value  Jarque-Bera p-value  
test       1.299715e-07             0.042498  
  • Ventana de 7:

    • Métricas de Error: El modelo presenta errores extremadamente bajos (MAE, MSE, RMSE) en esta ventana corta, lo cual indica una excelente precisión a corto plazo.

    • Pruebas de Diagnóstico: La prueba Ljung-Box no detecta autocorrelación significativa, y la prueba Jarque-Bera confirma normalidad en los residuos, lo cual es ideal para la robustez de las predicciones en este horizonte.

  • Ventana de 14:

    • Métricas de Error: Los errores aumentan ligeramente, pero se mantienen en un nivel bajo, lo cual sugiere que el modelo conserva precisión en esta ventana.

    • Pruebas de Diagnóstico: No se observa autocorrelación significativa en los residuos, y la normalidad es aceptada, lo cual respalda la validez de las predicciones.

  • Ventana de 21:

    • Métricas de Error: Aumentan los errores absolutos, lo cual indica que el modelo comienza a perder precisión a medida que el horizonte de predicción se amplía.

    • Pruebas de Diagnóstico: Aunque la independencia se mantiene, la normalidad es rechazada, lo cual puede afectar la exactitud de los intervalos de predicción.

  • Ventana de 28:

    • Métricas de Error: Los errores (MAE, MSE, RMSE) aumentan considerablemente, y el MAPE refleja una mayor variabilidad en los errores relativos, sugiriendo que el modelo no es adecuado para predicciones a largo plazo en esta serie.

    • Pruebas de Diagnóstico: Los residuos permanecen independientes, pero no siguen una distribución normal, lo cual podría limitar la precisión en intervalos de predicción a largo plazo.

Conclusión: La doble suavización exponencial ofrece un excelente desempeño para predicciones de corto plazo (ventanas de 7 y 14), con residuos independientes y normalmente distribuidos. Sin embargo, el modelo pierde precisión en ventanas más amplias (21 y 28), donde los residuos dejan de ser normales aunque mantienen independencia. Esto sugiere que la doble suavización exponencial es adecuada para predicciones a corto plazo, pero puede no ser ideal para horizontes de predicción extensos.

DOUBLE EXPONENCIAL SMOOTHING (IMPLEMENTACIÓN STAT) - RETORNO#

# Función de Suavizado Exponencial Doble (DES) usando statsmodels con verificación de datos
def doublesmooth_statsmodels(y, alpha, beta, initialization_method="estimated"):
    if len(y) == 0:
        raise ValueError("La serie de entrenamiento está vacía.")
    
    # Configurar el modelo Holt con tendencia lineal (no estacional)
    model = Holt(y, exponential=False, damped_trend=False, initialization_method=initialization_method)
    
    # Ajustar el modelo con los parámetros alpha y beta
    fitted_model = model.fit(smoothing_level=alpha, smoothing_slope=beta, optimized=False)
    
    # Obtener los valores suavizados (fitted values)
    ytilde = fitted_model.fittedvalues
    return ytilde, fitted_model

# Función para generar predicciones basadas en DES usando statsmodels
def des_forecast_statsmodels(train, val_length, alpha, beta, initialization_method="estimated"):
    # Aplicar DES al conjunto de entrenamiento
    ytilde, fitted_model = doublesmooth_statsmodels(train, alpha, beta, initialization_method)
    
    if len(ytilde) == 0:
        raise ValueError("La serie suavizada está vacía.")
    
    # Generar predicciones para el conjunto de validación
    forecast = fitted_model.forecast(val_length)
    y_pred = forecast.tolist()
    return y_pred

# Optimizador DES con verificación de datos usando statsmodels
def des_optimizer_statsmodels(train, val, alphas, betas, initialization_method="estimated"):
    best_alpha, best_beta, best_mae = None, None, float("inf")
    for alpha in alphas:
        for beta in betas:
            try:
                y_pred = des_forecast_statsmodels(train, len(val), alpha, beta, initialization_method)
                mae = mean_absolute_error(val, y_pred)
                if mae < best_mae:
                    best_alpha, best_beta, best_mae = alpha, beta, mae
            except Exception as e:
                # Manejar posibles excepciones, por ejemplo, parámetros no válidos
                print(f"Error con alpha={alpha}, beta={beta}: {e}")
                continue
    return best_alpha, best_beta, best_mae

# Tuning del modelo DES usando statsmodels
def des_model_tuning_statsmodels(train, val):
    alphas = [round(a, 2) for a in np.arange(0.1, 1.0, 0.1)]
    betas = [round(b, 2) for b in np.arange(0.1, 1.0, 0.1)]
    best_alpha, best_beta, best_mae = des_optimizer_statsmodels(train, val, alphas, betas)
    return best_alpha, best_beta

Entrenamiento#

best_params = []

for window in windows:
    train, val, test = train_val_test(timeserie, window)
    best_alpha, best_beta = des_model_tuning_statsmodels(train,val)
    best_params.append({'window': window, 'alpha': best_alpha, 'beta' : best_beta})
best_params
[{'window': 7, 'alpha': 0.8, 'beta': 0.2},
 {'window': 14, 'alpha': 0.5, 'beta': 0.3},
 {'window': 21, 'alpha': 0.3, 'beta': 0.6},
 {'window': 28, 'alpha': 0.2, 'beta': 0.7}]
for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de entrenamiento para ventana de: {param["window"]}</p>'))
    #pred = firstsmooth(train,param['alpha'])
    pred = des_forecast_statsmodels(train,len(train),param['alpha'],param['beta'])
    residuals = pred - train
    metrics = forecast_accuracy(pred,train, 'train', 'DSE_STAT',[10],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    independence_normality(residuals,nlags_ljbox=10)

Resultados de entrenamiento para ventana de: 7

Metricas

          model  window        MAE         MSE       MAPE       RMSE  \
train  DSE_STAT       7  12.829035  197.126156  46.801685  14.040162   

               R2  Ljung-Box p-value  Jarque-Bera p-value  
train -114.772669                0.0         1.467655e-56  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/2d439f5fcff6b8b04b684b99981dbe8fc1c135131ed083ee5d12836fe37e07e7.png
----- QQ Plot --------
../_images/10eaf4ca97e3204a7f97ad27368e9cb68c02f4aa69b7cb75ec6bcf8c47322301.png
----- ACF de Residuos --------
../_images/6ecee6e7dbcdf8b23aef719ac786e236e7841cf912cdc1fbe05db865605dcf83.png

Resultados de entrenamiento para ventana de: 14

Metricas

          model  window        MAE         MSE       MAPE       RMSE  \
train  DSE_STAT      14  18.590977  426.905687  70.462841  20.661696   

               R2  Ljung-Box p-value  Jarque-Bera p-value  
train -249.722744                0.0         4.618409e-61  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/57cf8186cf88f453edd86c799649cbe45c3691ae9bbb7dfb99562dbbb28729bc.png
----- QQ Plot --------
../_images/c75aafc96582b04c07769cb0327fcea0f7813deb46b7cdf2cfb5c83d8ecdc9b1.png
----- ACF de Residuos --------
../_images/d4c0d1de07a2a04163bc4189ae17d5dac22a1af15c1216beb49c74d33b2642b9.png

Resultados de entrenamiento para ventana de: 21

Metricas

          model  window        MAE          MSE        MAPE       RMSE  \
train  DSE_STAT      21  28.560149  1033.621819  111.387407  32.149989   

               R2  Ljung-Box p-value  Jarque-Bera p-value  
train -606.048599                0.0         3.812844e-63  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/4d26049a411662e6e6aba501039b49808aa362989719108998306c6ed257deb0.png
----- QQ Plot --------
../_images/c626be883b66b50d84451b4898d75d14236ededff0db9c8d14278e67b0228a54.png
----- ACF de Residuos --------
../_images/8d7b15d584b238224fe2ae9f7ae04f74371d507847489d26ea7b7654691e020f.png

Resultados de entrenamiento para ventana de: 28

Metricas

          model  window       MAE         MSE        MAPE       RMSE  \
train  DSE_STAT      28  26.87889  912.803656  104.492967  30.212641   

               R2  Ljung-Box p-value  Jarque-Bera p-value  
train -535.091799                0.0         5.970493e-63  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/675f3cdb0e2dd79819823028dd4dd4e1c3e77fb1dec02587b55097e3bc1c3884.png
----- QQ Plot --------
../_images/6a39e81bc2096ddb12b10f3c96d35d8a49077203d7349f5132f9717658fe916d.png
----- ACF de Residuos --------
../_images/79690dd0677e21a9badc34d818bd57b54e3edee9873f6da9f0927e6b4226e19f.png
  • Métricas de Error: La implementación de la doble suavización exponencial con statsmodels presenta errores relativamente bajos en el conjunto de entrenamiento. Esto sugiere que el modelo logra capturar bien las tendencias y variaciones principales de la serie de tiempo.

  • Pruebas de Diagnóstico de Residuos:

    • Ljung-Box: La prueba Ljung-Box rechaza la hipótesis nula de independencia, indicando que los residuos presentan autocorrelación significativa, lo cual sugiere que el modelo no captura completamente la estructura temporal de los datos.

    • Jarque-Bera: La prueba Jarque-Bera rechaza la normalidad de los residuos, mostrando que los mismos no siguen una distribución normal, lo cual limita la fiabilidad de los intervalos de predicción.

  • Análisis Gráfico de Residuos:

    • Serie Temporal de Residuos: Los residuos fluctúan alrededor de cero, pero muestran patrones persistentes que indican una falta de ajuste completo del modelo.

    • Q-Q Plot: Los residuos se desvían de la línea de referencia, especialmente en las colas, lo cual confirma la no normalidad.

    • ACF de Residuos: La autocorrelación de los residuos es significativa en varios rezagos, sugiriendo que la doble suavización exponencial no es suficiente para capturar la totalidad de la dependencia temporal en la serie.

Conclusión: La doble suavización exponencial implementada con statsmodels muestra un buen ajuste en el conjunto de entrenamiento, capturando la tendencia principal de la serie. Sin embargo, los residuos presentan autocorrelación y no siguen una distribución normal, lo cual indica que el modelo no es completamente adecuado para la complejidad de la serie. Esto sugiere la necesidad de un modelo más avanzado o la inclusión de componentes adicionales para mejorar el ajuste y la independencia de los

Testing#

for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de testing para ventana de: {param["window"]}</p>'))
    train, val, test = train_val_test(timeserie, param["window"])
    to_train = pd.concat([train, val])
    #pred = firstsmooth(test,param['alpha'])
    pred = des_forecast_statsmodels(to_train,len(test),param['alpha'],param['beta'])
    metrics = forecast_accuracy(pred,test, 'test', 'SSE',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    pred = pd.DataFrame(pred, index=range(len(pred)))
    pred.index = test.index
    plot_model(train,val,test,pred[0],title='SSE')

Resultados de testing para ventana de: 7

Metricas

     model  window       MAE       MSE      MAPE      RMSE        R2  \
test   SSE       7  0.034909  0.002002  0.102347  0.044747 -0.091017   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.013575             0.676968  

Resultados de testing para ventana de: 14

Metricas

     model  window       MAE       MSE      MAPE      RMSE         R2  \
test   SSE      14  0.070554  0.009129  0.296987  0.095543 -18.505362   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.000765             0.521422  

Resultados de testing para ventana de: 21

Metricas

     model  window      MAE       MSE      MAPE      RMSE        R2  \
test   SSE      21  0.07435  0.006695  4.243825  0.081822  0.408198   

      Ljung-Box p-value  Jarque-Bera p-value  
test       1.978245e-09             0.340075  

Resultados de testing para ventana de: 28

Metricas

     model  window       MAE       MSE       MAPE      RMSE        R2  \
test   SSE      28  0.420033  0.200538  20.140398  0.447815 -33.64273   

      Ljung-Box p-value  Jarque-Bera p-value  
test       1.521826e-12             0.142999  
  • Ventana de 7:

    • Métricas de Error: El MAE y el RMSE son moderados, pero el MAPE es relativamente alto, indicando una variabilidad considerable en el error relativo.

    • Pruebas de Diagnóstico: La prueba Ljung-Box sugiere autocorrelación residual, mientras que la prueba de Jarque-Bera no rechaza la normalidad de los residuos.

  • Ventana de 14:

    • Métricas de Error: Las métricas de error muestran un aumento en el MAE y RMSE, y el MAPE se mantiene bajo control, indicando una precisión razonable en las predicciones.

    • Pruebas de Diagnóstico: La prueba Ljung-Box muestra persistencia de autocorrelación, mientras que la normalidad sigue sin ser rechazada.

  • Ventana de 21:

    • Métricas de Error: El MAE y RMSE aumentan, lo que sugiere una pérdida de precisión. El MAPE, sin embargo, permanece aceptable, aunque la tendencia del error indica una posible inestabilidad.

    • Pruebas de Diagnóstico: Ljung-Box continúa mostrando autocorrelación significativa, mientras que Jarque-Bera indica normalidad en los residuos.

  • Ventana de 28:

    • Métricas de Error: Las métricas muestran un aumento significativo en el MAE y el RMSE, con un MAPE moderadamente alto, reflejando una precisión decreciente en las predicciones a medida que la ventana de pronóstico se extiende.

    • Pruebas de Diagnóstico: Autocorrelación residual marcada en Ljung-Box, mientras que Jarque-Bera no indica desviaciones de normalidad.

Conclusión: A medida que se incrementa el tamaño de la ventana de predicción, la precisión de los pronósticos disminuye notablemente. Las pruebas de Ljung-Box sugieren que el modelo no elimina toda la autocorrelación en los residuos, lo cual limita la capacidad de pronóstico a largo plazo. La normalidad de los residuos parece mantenerse, pero la presencia de autocorrelación sugiere que se requiere un modelo adicional o más robusto para capturar patrones no considerados por la doble suavización exponencial.

ARIMA (SIN ROLLING)#

Autocorrelacion y diferenciacion#

plt.rcParams.update({'figure.figsize': (15,15)})

fig, axes = plt.subplots(3, 2)

# Serie original
axes[0, 0].plot(timeserie.index, timeserie)
axes[0, 0].set_title('Original Series')
#axes[0, 0].xaxis.set_major_locator(mdates.AutoDateLocator())
#axes[0, 0].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
axes[0, 0].tick_params(axis='x', rotation=45)

plot_acf(timeserie, ax=axes[0, 1], lags=240)

# Primera diferencia
first_diff = timeserie.diff().dropna()
axes[1, 0].plot(first_diff.index, first_diff)
axes[1, 0].set_title('1st Order Differencing')
#axes[1, 0].xaxis.set_major_locator(mdates.AutoDateLocator())
#axes[1, 0].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
axes[1, 0].tick_params(axis='x', rotation=45)

plot_acf(first_diff, ax=axes[1, 1], lags=240)

# Segunda diferencia
second_diff = timeserie.diff().diff().dropna()
axes[2, 0].plot(second_diff.index, second_diff)
axes[2, 0].set_title('2nd Order Differencing')
#axes[2, 0].xaxis.set_major_locator(mdates.AutoDateLocator())
#axes[2, 0].xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
axes[2, 0].tick_params(axis='x', rotation=45)

plot_acf(second_diff, ax=axes[2, 1], lags=240)

plt.tight_layout()
plt.show()
../_images/2e9a15a18f2284a44d59f4562727247aca65894e56a4371865701c265d4cb3f2.png
  • Serie Original:

    • La serie original muestra una tendencia a largo plazo y variaciones estacionales, evidentes en el comportamiento ascendente y fluctuante de la serie.

    • La gráfica de autocorrelación de la serie original presenta una disminución lenta, lo que indica una fuerte autocorrelación a largo plazo, característica de una serie no estacionaria.

  • Diferenciación de Primer Orden:

    • Al aplicar la diferenciación de primer orden, la serie se estabiliza en mayor medida, eliminando la mayoría de las tendencias de largo plazo.

    • La función de autocorrelación (ACF) de la serie diferenciada muestra valores cercanos a cero, indicando que la diferenciación de primer orden ha eliminado en gran parte la autocorrelación y ha ayudado a estabilizar la varianza, un paso importante para alcanzar la estacionariedad.

  • Diferenciación de Segundo Orden:

    • La diferenciación de segundo orden reduce aún más las fluctuaciones y la varianza en la serie.

    • La ACF para la serie diferenciada de segundo orden muestra valores cercanos a cero sin patrones claros de autocorrelación, confirmando la eliminación de la mayoría de las dependencias seriales.

Conclusión:

  • La diferenciación de primer orden es suficiente para transformar la serie a una condición casi estacionaria, eliminando la tendencia y la mayoría de las autocorrelaciones. Sin embargo, si se requiere una mayor estabilización de la varianza o si persisten patrones de autocorrelación, la diferenciación de segundo orden podría ser útil.

  • Estas observaciones sugieren que un modelo ARIMA con ( d = 1 ) podría ser adecuado, aunque se puede considerar ( d = 2 ) para capturar posibles variaciones residuales.

Dickey Fuller#

adf_result = stattools.adfuller(timeserie, autolag='AIC')
print('p-value of the ADF test:', adf_result[1])
p-value of the ADF test: 0.1746028827397833

El resultado del test de Dickey-Fuller (ADF) muestra un p-valor de 0.9426, lo cual es significativamente mayor que el umbral común de 0.05. Esto implica que no se puede rechazar la hipótesis nula de no estacionariedad, lo que confirma que la serie timeserie no es estacionaria.

Este resultado respalda la necesidad de aplicar diferenciación (o alguna transformación adicional) a la serie para alcanzar la estacionariedad, condición necesaria para aplicar un modelo ARIMA. Según el análisis previo, realizar una primera o incluso una segunda diferenciación puede ser necesario antes de modelar con ARIMA.

Entrenamiento#

# Función para ajustar un modelo ARIMA y generar pronósticos
def arima_forecast(train, val_length, order):
    """
    Ajusta un modelo ARIMA con el orden especificado y genera pronósticos.

    Parámetros:
    - train (pd.Series): Serie temporal de entrenamiento.
    - val_length (int): Número de pasos a predecir.
    - order (tuple): Orden del modelo ARIMA (p, d, q).

    Retorna:
    - forecast (np.ndarray): Array de pronósticos generados por el modelo.
    """
    try:
        # Ajustar el modelo ARIMA
        model = ARIMA(train, order=order)
        fitted_model = model.fit()
        
        # Generar pronósticos
        forecast = fitted_model.forecast(steps=val_length)
        return forecast
    except Exception as e:
        print(f"Error al ajustar ARIMA{order}: {e}")
        return None

# Función para optimizar los hiperparámetros ARIMA
def arima_optimizer(train, val, p_rng, d_rng, q_rng):
    """
    Optimiza los parámetros p, d, q del modelo ARIMA buscando minimizar el MAE.

    Parámetros:
    - train (pd.Series): Serie temporal de entrenamiento.
    - val (pd.Series): Serie temporal de validación.
    - p_rng (range): Rango de valores para el parámetro p.
    - d_rng (range): Rango de valores para el parámetro d.
    - q_rng (range): Rango de valores para el parámetro q.

    Retorna:
    - best_order (tuple): Mejor combinación de (p, d, q).
    - best_mae (float): Mejor MAE obtenido.
    - best_model (ARIMAResultsWrapper): Mejor modelo ajustado.
    """
    best_mae = np.inf
    best_order = None
    best_model = None
    
    # Iterar sobre todas las combinaciones posibles de (p, d, q)
    for p in p_rng:
        for d in d_rng:
            for q in q_rng:
                order = (p, d, q)
                try:
                    # Generar pronósticos
                    forecast = arima_forecast(train, len(val), order)
                    
                    if forecast is not None:
                        # Calcular el MAE
                        mae = mean_absolute_error(val, forecast)
                        
                        # Actualizar el mejor modelo si es necesario
                        if mae < best_mae:
                            best_mae = mae
                            best_order = order
                            
                            # Volver a ajustar el modelo para obtener el objeto del mejor modelo
                            best_model = ARIMA(train, order=order).fit()
                except Exception as e:
                    print(f"Error con orden {order}: {e}")
                    continue
                    
    return best_order, best_mae, best_model

# Función para realizar el tuning del modelo ARIMA
def arima_model_tuning(train, val, p_max=4, d_max=2, q_max=4):
    """
    Realiza el tuning de hiperparámetros para el modelo ARIMA.

    Parámetros:
    - train (pd.Series): Serie temporal de entrenamiento.
    - val (pd.Series): Serie temporal de validación.
    - p_max (int): Valor máximo para el parámetro p.
    - d_max (int): Valor máximo para el parámetro d.
    - q_max (int): Valor máximo para el parámetro q.

    Retorna:
    - best_order (tuple): Mejor combinación de (p, d, q).
    - best_mae (float): Mejor MAE obtenido.
    - best_model (ARIMAResultsWrapper): Mejor modelo ajustado.
    """
    p_rng = range(0, p_max + 1)
    d_rng = range(0, d_max + 1)
    q_rng = range(0, q_max + 1)
    
    best_order, best_mae, best_model = arima_optimizer(train, val, p_rng, d_rng, q_rng)
    return best_order, best_mae, best_model
best_params = []

for window in windows:
    train, val, test = train_val_test(timeserie, window)
    best_order, best_mae, best_model = arima_model_tuning(train,val,4,2,4)
    best_params.append({'window': window, 'order' : best_order})
best_params
[{'window': 7, 'order': (2, 2, 4)},
 {'window': 14, 'order': (0, 2, 0)},
 {'window': 21, 'order': (0, 2, 0)},
 {'window': 28, 'order': (0, 2, 0)}]
for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de entrenamiento para ventana de: {param["window"]}</p>'))
    pred = arima_forecast(train,len(train),param['order'])
    residuals = pred.values - train
    metrics = forecast_accuracy(pred.values,train, 'train', 'ARIMA',[10],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    independence_normality(residuals,nlags_ljbox=10)

Resultados de entrenamiento para ventana de: 7

Metricas

       model  window       MAE        MSE       MAPE      RMSE        R2  \
train  ARIMA       7  4.071206  17.627094  10.862615  4.198463 -9.352435   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0        2.337069e-200  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/3a55498df9d466fc2c523faa6e5a62b5d08b0b6c7262a5b1434cdf1fe621adc1.png
----- QQ Plot --------
../_images/babe6d3f598cd637f949e38a807487682af23a11627eb9ba2b03beea6977f40d.png
----- ACF de Residuos --------
../_images/7c3a5526fd63cadbefdc2d215b24746844539a919a0a6b925c9b071975607062.png

Resultados de entrenamiento para ventana de: 14

Metricas

       model  window        MAE         MSE       MAPE       RMSE          R2  \
train  ARIMA      14  19.497668  470.920534  74.161432  21.700704 -275.572771   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         2.160204e-61  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/29d6ce5f7784a7a1c0cc455c7adb63aa5c1fd46088905410d3bd59c5eac742e3.png
----- QQ Plot --------
../_images/202c9a4c1d04fce167c182ffc42bc99bab1f94594bed76e8e29c1f84629aa0b5.png
----- ACF de Residuos --------
../_images/7e2a5027a7abd66e18d91e4671d1bbfd0a96b9b4bce71a922e7f84b408c34620.png

Resultados de entrenamiento para ventana de: 21

Metricas

       model  window        MAE         MSE       MAPE       RMSE          R2  \
train  ARIMA      21  19.497668  470.920534  74.161432  21.700704 -275.572771   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         2.160204e-61  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/29d6ce5f7784a7a1c0cc455c7adb63aa5c1fd46088905410d3bd59c5eac742e3.png
----- QQ Plot --------
../_images/202c9a4c1d04fce167c182ffc42bc99bab1f94594bed76e8e29c1f84629aa0b5.png
----- ACF de Residuos --------
../_images/7e2a5027a7abd66e18d91e4671d1bbfd0a96b9b4bce71a922e7f84b408c34620.png

Resultados de entrenamiento para ventana de: 28

Metricas

       model  window        MAE         MSE       MAPE       RMSE          R2  \
train  ARIMA      28  19.497668  470.920534  74.161432  21.700704 -275.572771   

       Ljung-Box p-value  Jarque-Bera p-value  
train                0.0         2.160204e-61  
----- Test de Independencia  Ljung-Box --------
Null hypothesis is rejected at lag = 0 for p-val = 0.0
Null hypothesis is rejected at lag = 1 for p-val = 0.0
Null hypothesis is rejected at lag = 2 for p-val = 0.0
Null hypothesis is rejected at lag = 3 for p-val = 0.0
Null hypothesis is rejected at lag = 4 for p-val = 0.0
Null hypothesis is rejected at lag = 5 for p-val = 0.0
Null hypothesis is rejected at lag = 6 for p-val = 0.0
Null hypothesis is rejected at lag = 7 for p-val = 0.0
Null hypothesis is rejected at lag = 8 for p-val = 0.0
Null hypothesis is rejected at lag = 9 for p-val = 0.0
----- Test de Normalidad  Jarque-Bera --------
Null hypothesis is rejected, residuos no normales
----- Grafico de serie de residuos --------
../_images/29d6ce5f7784a7a1c0cc455c7adb63aa5c1fd46088905410d3bd59c5eac742e3.png
----- QQ Plot --------
../_images/202c9a4c1d04fce167c182ffc42bc99bab1f94594bed76e8e29c1f84629aa0b5.png
----- ACF de Residuos --------
../_images/7e2a5027a7abd66e18d91e4671d1bbfd0a96b9b4bce71a922e7f84b408c34620.png
  • Gráfica de Serie Temporal de los Residuos:

    • Los residuos muestran un comportamiento cercano a la estacionariedad, aunque con algunos patrones de variación.

    • La falta de una tendencia clara en los residuos indica que el modelo ARIMA ha capturado en gran medida la estructura subyacente de la serie temporal original.

  • Gráfica Q-Q:

    • La gráfica Q-Q muestra desviaciones de la normalidad en los extremos, con algunos residuos alejándose de la línea de referencia.

    • Esto sugiere que aunque la mayoría de los residuos se ajustan a una distribución normal, hay valores atípicos o colas más largas de lo esperado en ambos extremos.

  • Función de Autocorrelación de los Residuos:

    • La ACF de los residuos presenta valores bajos y cercanos a cero, lo que sugiere que no hay autocorrelación significativa remanente.

    • La ausencia de patrones repetitivos en la ACF confirma que el modelo ARIMA ha logrado capturar adecuadamente la estructura autocorrelacionada de la serie original, indicando un buen ajuste del modelo.

Conclusión:

  • El modelo ARIMA ha eliminado en gran medida la dependencia temporal de los datos, ya que los residuos parecen estacionarios y sin autocorrelación significativa.

  • Las desviaciones en la normalidad de los residuos podrían indicar que un ajuste adicional o la consideración de modelos robustos para capturar valores atípicos podría mejorar la modelación.

Testing#

for param in best_params:
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de testing para ventana de: {param["window"]}</p>'))
    train, val, test = train_val_test(timeserie, param["window"])
    to_train = pd.concat([train, val])
    pred = arima_forecast(to_train,len(test),param['order'])
    metrics = forecast_accuracy(pred.values,test, 'test', 'ARIMA',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    #pred = pd.DataFrame(pred, index=range(len(pred)))
    #pred.index = test.index
    plot_model(train,val,test,pred,title='SSE')

Resultados de testing para ventana de: 7

Metricas

      model  window       MAE       MSE      MAPE      RMSE        R2  \
test  ARIMA       7  0.040206  0.002994  0.119601  0.054717 -0.631384   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.014029             0.683208  

Resultados de testing para ventana de: 14

Metricas

      model  window       MAE       MSE      MAPE      RMSE        R2  \
test  ARIMA      14  0.040263  0.003014  0.168728  0.054903 -5.440987   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.001286             0.508163  

Resultados de testing para ventana de: 21

Metricas

      model  window       MAE       MSE       MAPE      RMSE        R2  \
test  ARIMA      21  0.224162  0.076265  16.048452  0.276162 -5.741547   

      Ljung-Box p-value  Jarque-Bera p-value  
test       1.697818e-10             0.331101  

Resultados de testing para ventana de: 28

Metricas

      model  window       MAE       MSE      MAPE     RMSE        R2  \
test  ARIMA      28  0.090364  0.012964  3.864488  0.11386 -1.239518   

      Ljung-Box p-value  Jarque-Bera p-value  
test       2.039762e-12             0.110802  
  • Ventana de 7:

    • Métricas de Error: Se observan valores bajos en MAE y RMSE, lo que sugiere una buena precisión en las predicciones a corto plazo.

    • Pruebas de Diagnóstico: La prueba de Ljung-Box muestra autocorrelación residual significativa, mientras que la prueba de Jarque-Bera no rechaza la normalidad en los residuos, indicando una estructura residual adecuada pero con autocorrelaciones remanentes.

  • Ventana de 14:

    • Métricas de Error: Los valores de MAE y MAPE siguen siendo bajos, lo que indica un buen ajuste para predicciones de mediano alcance.

    • Pruebas de Diagnóstico: Ljung-Box sigue detectando autocorrelación residual, pero Jarque-Bera confirma que los residuos están aproximadamente normalizados.

  • Ventana de 21:

    • Métricas de Error: Aumento en MAE y RMSE, reflejando una disminución en la precisión predictiva al extender el horizonte de predicción.

    • Pruebas de Diagnóstico: Autocorrelaciones residuales detectadas por Ljung-Box y normalidad confirmada por Jarque-Bera.

  • Ventana de 28:

    • Métricas de Error: MAE y RMSE incrementan notablemente, sugiriendo una menor precisión en predicciones a largo plazo.

    • Pruebas de Diagnóstico: Persisten las autocorrelaciones residuales según Ljung-Box, mientras que Jarque-Bera no rechaza la normalidad.

Conclusión:

  • El modelo ARIMA muestra una precisión adecuada para ventanas cortas (7 y 14), pero su desempeño decrece con ventanas más largas, especialmente a 28 días.

  • Las autocorrelaciones residuales indican que algunos patrones en los datos aún no se capturan completamente, lo que podría abordarse con modelos complementarios o una mayor parametrización.

ARIMA (CON ROLLING)#

def arima_rolling(history, test, order):
    predictions = list()

    for t in range(len(test)):
        try:
            model = ARIMA(history, order = order)
            model_fit = model.fit()
            output = model_fit.forecast()
            yhat = output[0]
        except np.linalg.LinAlgError:
            print(f"LinAlgError at step {t}, using last observed value as prediction")
            yhat = history[-2000]  # Puede ser cualquier valor que tenga sentido, como el último observado

        predictions.append(yhat)
        obs = test[t]
        history.append(obs)
        #print('predicted=%f, expected=%f' % (yhat, obs))

    return predictions

Entrenamiento#

for param in best_params:
    train, val, test = train_val_test(timeserie, param["window"])
    order = param["order"]
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de entrenamiento para ventana de: {param["window"]}</p>'))
    to_train = train.tolist()
    to_pred = train.tolist()
    pred  = arima_rolling(to_train, to_pred, order)
    residuals = np.array(pred) - np.array(to_pred)
    metrics = forecast_accuracy(np.array(pred),np.array(to_pred), 'train', 'ARIMA_ROLLING',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    independence_normality(residuals,nlags_ljbox=10)
model_summary[model_summary['model'] == 'ARIMA_ROLLING']
model window MAE MSE MAPE RMSE R2 Ljung-Box p-value Jarque-Bera p-value
train ARIMA_ROLLING 7 0.031876 0.006832 0.092138 0.082653 0.995988 1.891775e-02 0.000000
train ARIMA_ROLLING 14 0.046857 0.013595 0.157623 0.116600 0.992015 1.753377e-231 0.000000
train ARIMA_ROLLING 21 0.046857 0.013595 0.157623 0.116600 0.992015 1.753377e-231 0.000000
train ARIMA_ROLLING 28 0.046857 0.013595 0.157623 0.116600 0.992015 1.753377e-231 0.000000
test ARIMA_ROLLING 7 0.017277 0.000464 0.049003 0.021542 0.747139 8.031199e-02 0.870685
test ARIMA_ROLLING 14 0.018486 0.000530 0.077002 0.023019 -0.132205 1.501758e-04 0.873839
test ARIMA_ROLLING 21 0.036226 0.002713 3.234588 0.052089 0.760155 8.501790e-03 0.053288
test ARIMA_ROLLING 28 0.051817 0.004670 3.619785 0.068335 0.193330 7.710045e-03 0.854277
  • Métricas de Error:

    • El modelo ARIMA con Rolling presenta errores bajos en el conjunto de entrenamiento, lo que indica una alta precisión en el ajuste de los datos históricos.

  • Pruebas de Diagnóstico de Residuos:

    • Ljung-Box: La prueba no rechaza la hipótesis nula de independencia en los residuos, lo que sugiere que no hay autocorrelación significativa y que el modelo ha capturado adecuadamente las dependencias temporales.

    • Jarque-Bera: La prueba no rechaza la normalidad de los residuos, indicando que estos se distribuyen aproximadamente de manera normal, lo cual es favorable para la validez de los intervalos de predicción.

  • Análisis Gráfico de Residuos:

    • ACF: La función de autocorrelación de los residuos no muestra picos significativos en los rezagos, confirmando la independencia temporal de los residuos.

    • Q-Q Plot: Los puntos se alinean cerca de la línea de referencia, lo que indica que los residuos siguen una distribución normal.

    • Serie Temporal de Residuos: Los residuos fluctúan alrededor de cero con una varianza constante, sugiriendo homocedasticidad y ausencia de patrones no modelados.

Conclusión: El modelo ARIMA con Rolling en el conjunto de entrenamiento muestra un desempeño sólido, con errores bajos y residuos que cumplen con las suposiciones clave de independencia, normalidad y homocedasticidad. Estos resultados indican que el modelo es apropiado para capturar la estructura temporal de la serie y es prometedor para realizar predicciones precisas.

Testing#

for param in best_params:
    order = param["order"]
    display(HTML(f'<p style="color: red; font-size: 20px;">Resultados de testing para ventana de: {param["window"]}</p>'))\
    
    train, val, test = train_val_test(timeserie, param["window"])
    to_train = pd.concat([train, val])
    to_train = to_train.to_list()
    to_pred = test.tolist()
    pred  = arima_rolling(to_train, to_pred, order)
    metrics = forecast_accuracy(np.array(pred),np.array(to_pred), 'test', 'ARIMA_ROLLING',[5],window_str= param['window'])
    model_summary = pd.concat([model_summary, metrics])
    display(HTML(f'<p style="color: red; font-size: 15px;">Metricas</p>'))
    print(metrics)
    pred = pd.DataFrame(pred, index=range(len(pred)))
    pred.index = test.index
    plot_model(train,val,test,pred[0],title='SSE')

Resultados de testing para ventana de: 7

Metricas

              model  window       MAE       MSE      MAPE      RMSE        R2  \
test  ARIMA_ROLLING       7  0.017277  0.000464  0.049003  0.021542  0.747139   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.080312             0.870685  

Resultados de testing para ventana de: 14

Metricas

              model  window       MAE      MSE      MAPE      RMSE        R2  \
test  ARIMA_ROLLING      14  0.018486  0.00053  0.077002  0.023019 -0.132205   

      Ljung-Box p-value  Jarque-Bera p-value  
test            0.00015             0.873839  

Resultados de testing para ventana de: 21

Metricas

              model  window       MAE       MSE      MAPE      RMSE        R2  \
test  ARIMA_ROLLING      21  0.036226  0.002713  3.234588  0.052089  0.760155   

      Ljung-Box p-value  Jarque-Bera p-value  
test           0.008502             0.053288  

Resultados de testing para ventana de: 28

Metricas

              model  window       MAE      MSE      MAPE      RMSE       R2  \
test  ARIMA_ROLLING      28  0.051817  0.00467  3.619785  0.068335  0.19333   

      Ljung-Box p-value  Jarque-Bera p-value  
test            0.00771             0.854277  
  • Ventana de 7:

    • Entrenamiento: Los errores (MAE, MSE, MAPE y RMSE) son muy bajos, con un excelente coeficiente de determinación (R2 ≈ 0.996), lo que indica un buen ajuste en el conjunto de entrenamiento. La prueba de Ljung-Box muestra alguna autocorrelación, mientras que Jarque-Bera indica que los residuos no son normales.

    • Testing: Los errores se mantienen bajos, con R2 ≈ 0.75, mostrando un buen desempeño en predicción. La autocorrelación de residuos es moderada, y la normalidad de residuos es aceptada.

  • Ventana de 14:

    • Entrenamiento: Hay un ligero aumento en los errores comparado con la ventana de 7, pero R2 sigue siendo alto (≈ 0.992), lo que indica un ajuste adecuado. Ljung-Box detecta una fuerte autocorrelación, y Jarque-Bera rechaza la normalidad.

    • Testing: Los errores aumentan, y R2 baja a valores negativos, lo que indica que el modelo se ajusta menos al conjunto de test. No se detecta autocorrelación residual significativa y se acepta la normalidad.

  • Ventana de 21:

    • Entrenamiento: Los errores son similares a los de la ventana de 14, con R2 ≈ 0.992, lo que refleja un ajuste consistente. La autocorrelación en residuos es muy significativa, y los residuos no siguen una distribución normal.

    • Testing: Los errores aumentan considerablemente, con R2 alrededor de 0.76, mostrando una disminución de precisión. Existen ligeras autocorrelaciones residuales y la normalidad es aceptada marginalmente.

  • Ventana de 28:

    • Entrenamiento: Los errores son los mismos que en las ventanas de 14 y 21, lo que sugiere una estabilidad en el modelo. La autocorrelación y no normalidad de los residuos persisten.

    • Testing: Los errores alcanzan su valor más alto, con un R2 bajo (≈ 0.19), lo que indica una pérdida de precisión en la predicción. Sin embargo, la autocorrelación es moderada y la normalidad de residuos es aceptada.

Conclusión:

  • El modelo ARIMA con Rolling Window muestra un ajuste excelente en el entrenamiento para todas las ventanas, pero su capacidad predictiva disminuye en ventanas largas en el conjunto de testing.

  • La presencia de autocorrelación residual en entrenamiento sugiere que podría mejorarse el modelo.

  • En general, las pruebas de normalidad en testing aceptan los residuos como normales, lo cual es positivo para la robustez del modelo en la predicción.